#pragma once


#include "ASMInstruction.hpp"
#include "Module.hpp"
#include "Register.hpp"
#include <map>
#include <utility>
#include <set>
#include <unordered_set>
#include <cassert>
#include <cstring>
#include <string>

#define STACK_ALIGN(x) ALIGN(x, 16)
#define CONST_0 ConstantInt::get(0, m)
#define FP "s0"
#define SP "sp"
#define RA_reg "ra"
#define R_USABLE (17 - 3)
#define FR_USABLE (24 - 2)
#define ARG_R 8

using std::map;
using std::pair;
using std::set;
using std::string;
using std::to_string;
using std::vector;

class CodeGen {
  public:
    explicit CodeGen(Module *module) : m(module) {}

    string print() const;

    void run();

    template <class... Args> void append_inst(Args... arg) {
        output.emplace_back(arg...);
    }

    void append_inst(const char *inst, std::initializer_list<string> args,
                ASMInstruction::InstType ty = ASMInstruction::Instruction) {
        static const std::unordered_set<std::string> offset_base_insts = {//需要使用 offset(base) 地址寻址格式的指令
            "lb", "lbu", "lh", "lhu", "lw", "lwu", "ld",
            "sb", "sh", "sw", "sd",
            "flw", "fld", "fsw", "fsd"
        };
        auto content = string(inst) + " ";
        if (offset_base_insts.count(inst) && args.size() == 3) {
            // 假设格式为 {"rd", "base", "offset"}
            auto it = args.begin();
            std::string reg = *it++;
            std::string base = *it++;
            std::string offset = *it;
            content += reg + ", " + offset + "(" + base + ")";
        } else {
            for (const auto &arg : args) {
                content += arg + ", ";
            }
            // 移除最后的 ", "
            content.pop_back();
            content.pop_back();
        }
        output.emplace_back(content, ty);
    }

  private:
    void allocate();
    void copy_stmt(); // for phi copy

    // 向寄存器中装载数据
    void load_to_greg(Value *, const Reg &);
    void load_to_freg(Value *, const FReg &);
    void load_from_stack_to_greg(Value *, const Reg &);

    // 向寄存器中加载立即数
    void load_large_int32(int32_t, const Reg &);
    void load_large_int64(int64_t, const Reg &);
    void load_float_imm(float, const FReg &);

    // 将寄存器中的数据保存回栈上
    void store_from_greg(Value *, const Reg &);
    void store_from_freg(Value *, const FReg &);

    void global_array_int(ConstantArray * init_val);
    void global_array_float(ConstantArray * init_val);


    void gen_prologue();
    void gen_ret();
    void gen_br();
    void gen_binary();
    void gen_float_binary();
    void gen_alloca();
    void gen_load();
    void gen_store();
    void gen_icmp();
    void gen_fcmp();
    void gen_zext();
    void gen_call();
    void gen_gep();
    void gen_sitofp();
    void gen_fptosi();
    void gen_epilogue();
    string tmpregname(int i, bool is_float) const {//只会用到t0 or t1
        assert(i == 0 or i == 1);
        return (is_float ? "ft" : "t") + to_string(i);
    }

    static pair<int, int> immRange(int bit, bool u) {
        pair<int, int> res;
        if (u) {
            res.first = 0;
            res.second = (1 << bit) - 1;
        } else {
            bit--;
            res.first = -(1 << bit);
            res.second = (1 << bit) - 1;
        }

        return res;
    }
    static string label_name(BasicBlock *bb) {
        return "." + bb->get_parent()->get_name() + "_" + bb->get_name();
    }
    static string func_exit_label_name(Function *func) {
        return func->get_name() + "_exit";
    }
    static string fcmp_label_name(BasicBlock *bb, unsigned cnt) {
        return label_name(bb) + "_fcmp_" + to_string(cnt);
    }

    void makeSureInRange(string instr_ir,
        string reg1,
        string reg2,
        int imm,
        string tinstr,
        int tid = 0,
        int bits = 12,
        bool u = false){
            auto treg = tmpregname(tid, false);
            assert(treg != reg2 && "it's possible to write tid before reg2's use");

            // 获取立即数的合法范围
            auto [l, h] = immRange(bits, u);

            // 如果立即数在合法范围内，直接使用
            if (l <= imm && imm <= h){
                append_inst(instr_ir.c_str(), {reg1, reg2, to_string(imm)});
            } else {
                int imm_high = (imm + (1 << 11)) >> 12;  // rounding
                int imm_low = imm - (imm_high << 12);

                // 强制转为无符号 20-bit
                int lui_imm = imm_high & ((1 << 20) - 1);
                // 构造指令
                append_inst("lui", {treg, to_string(lui_imm)});
                append_inst("addi", {treg, treg, to_string(imm_low)});

                // 根据类型决定后续操作
                if (tinstr == "ldx" || tinstr == "stx") {
                    append_inst("add", {treg, reg2, treg});
                    append_inst(instr_ir.c_str(), {reg1, treg, "0"});
                } else {
                    append_inst(tinstr.c_str(), {reg1, treg, reg2});
                }
            }
        }

    struct {
        /* 随着ir遍历设置 */
        Function *func{nullptr};    // 当前函数
        Instruction *inst{nullptr}; // 当前指令
        BasicBlock *bb{nullptr}; // 当前BB
        /* 在allocate()中设置 */
        unsigned frame_size{0}; // 当前函数的栈帧大小
        std::unordered_map<Value *, int> offset_map{}; // 指针相对 fp 的偏移
        std::unordered_map<Value *, int> offset_call{}; // 指针相对 sp 的偏移
        unsigned fcmp_cnt{0}; // fcmp 的计数器, 用于创建 fcmp 需要的 label
        std::unordered_map<Value *, int> array_start_offset{};

        void clear() {
            func = nullptr;
            inst = nullptr;
            bb =nullptr;
            fcmp_cnt = 0;
            frame_size = 0;
            offset_map.clear();
            offset_call.clear();
            array_start_offset.clear();
        }

    } context;

    Module *m;
    std::list<ASMInstruction> output;//输出RISCV
};

